#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
 安装管理器 (Python 3.11+)

自动化安装  及其生态系统的脚本
处理依赖、仓库和配置

作者:  开发团队
版本: 2.0
日期: 2025-10-31
Python 版本: 3.11+
"""

import os
import sys
import subprocess
import argparse
import shutil
from pathlib import Path


# ============================================
# 配置类
# ============================================

class InstallConfig:
    """安装配置中心"""

    # 基础路径
    ROOT = os.path.dirname(os.path.abspath(__file__))
    SRC = os.path.join(ROOT, 'src')
    WEBAPP = os.path.join(ROOT, 'webapp')

    # 依赖文件
    DEPS_FILE = 'requirements_python311.txt'
    TEST_FILE = 'requirements-test.txt'

    # Git 仓库地址
    REPOS = {
        '神经网络工具': 'https://github.com/Neurosim-lab/神经网络工具.git',
        'workspace': 'https://github.com/Neurosim-lab/神经网络工具_workspace',
        'geppetto_meta': 'https://github.com/MetaCell/geppetto-meta/'
    }

    # 组件名称
    COMPONENTS = {
        '神经网络工具': '神经网络工具',
        'workspace': 'workspace',
        'geppetto_meta': 'geppetto-meta',
        'pygeppetto': 'pygeppetto',
        'jupyter_geppetto': 'jupyter-geppetto'
    }

    # 环境配置
    JUPYTER_CFG = os.path.join(ROOT, '.jupyter-config')


# 设置环境变量
os.environ['JUPYTER_CONFIG_DIR'] = InstallConfig.JUPYTER_CFG


# ============================================
# 工具函数
# ============================================

class Logger:
    """彩色日志记录器"""

    COLORS = {
        'info': '\033[36m',      # 青色
        'success': '\033[32m',   # 绿色
        'warning': '\033[33m',   # 黄色
        'error': '\033[31m',     # 红色
        'header': '\033[35;1m',  # 紫色加粗
        'reset': '\033[0m'
    }

    @staticmethod
    def log(message, level='info'):
        """打印彩色日志消息"""
        color = Logger.COLORS.get(level, Logger.COLORS['info'])
        reset = Logger.COLORS['reset']
        prefix = '⚙️' if level == 'info' else '✓' if level == 'success' else '⚠️' if level == 'warning' else '✗'
        print(f"{color}{prefix} {message}{reset}")
        sys.stdout.flush()

    @staticmethod
    def header(message):
        """打印标题消息"""
        Logger.log(f"\n{'='*60}\n{message}\n{'='*60}", 'header')


class CommandRunner:
    """命令执行器（带错误处理）"""

    @staticmethod
    def run(cmd, cwd=None, check=True):
        """执行命令并处理错误"""
        cwd = cwd or InstallConfig.ROOT
        Logger.log(f"执行命令: {' '.join(cmd) if isinstance(cmd, list) else cmd}")

        try:
            result = subprocess.run(
                cmd,
                cwd=cwd,
                check=check,
                capture_output=False,
                text=True
            )
            return result.returncode == 0
        except subprocess.CalledProcessError as e:
            Logger.log(f"命令执行失败，退出码 {e.returncode}", 'error')
            if check:
                raise SystemExit(f"安装失败于: {' '.join(cmd)}")
            return False
        except Exception as e:
            Logger.log(f"意外错误: {e}", 'error')
            if check:
                raise
            return False


class GitManager:
    """Git 操作管理器"""

    @staticmethod
    def clone_repo(url, target_dir, branch=None):
        """克隆 Git 仓库"""
        if os.path.exists(target_dir):
            Logger.log(f"目录已存在，跳过克隆: {target_dir}", 'warning')
            return True

        Logger.log(f"克隆仓库: {url}")
        cmd = ['git', 'clone', url, target_dir]

        if not CommandRunner.run(cmd, check=False):
            return False

        if branch and os.path.exists(os.path.join(target_dir, '.git')):
            GitManager.checkout_branch(target_dir, branch)

        return True

    @staticmethod
    def checkout_branch(repo_dir, branch):
        """切换到指定分支"""
        Logger.log(f"切换分支: {branch}")

        CommandRunner.run(['git', 'fetch'], cwd=repo_dir, check=False)
        CommandRunner.run(['git', 'checkout', branch], cwd=repo_dir, check=False)
        CommandRunner.run(['git', 'pull', 'origin', branch], cwd=repo_dir, check=False)


# ============================================
# 安装组件
# ============================================

class DependencyInstaller:
    """Python 依赖安装器"""

    @staticmethod
    def install_requirements(use_mirror=False):
        """安装主要依赖"""
        Logger.header("安装 Python 依赖包")

        req_file = os.path.join(InstallConfig.ROOT, InstallConfig.DEPS_FILE)

        if not os.path.exists(req_file):
            Logger.log(f"依赖文件未找到: {req_file}", 'error')
            raise SystemExit("缺少依赖文件，无法继续")

        Logger.log(f"使用依赖文件: {InstallConfig.DEPS_FILE}")

        # 构建命令
        cmd = ['pip', 'install', '-r', InstallConfig.DEPS_FILE]

        # 如果使用镜像源
        if use_mirror:
            Logger.log("使用清华镜像源")
            cmd.extend(['-i', 'https://pypi.tuna.tsinghua.edu.cn/simple',
                       '--trusted-host', 'pypi.tuna.tsinghua.edu.cn'])

        # 尝试安装
        if not CommandRunner.run(cmd, check=False):
            if not use_mirror:
                Logger.log("安装失败，尝试使用镜像源重试...", 'warning')
                return DependencyInstaller.install_requirements(use_mirror=True)
            else:
                Logger.log("安装失败", 'error')
                raise SystemExit("依赖安装失败")

        Logger.log("依赖包安装成功", 'success')

    @staticmethod
    def install_ui_package():
        """以可编辑模式安装包"""
        Logger.header("安装包")

        cmd = ['pip', 'install', '-e', '.', '--no-deps']
        CommandRunner.run(cmd)
        Logger.log("包安装成功", 'success')


class ComponentInstaller:
    """可选组件安装器"""

    @staticmethod
    def setup_source_dir():
        """创建源码目录（如果需要）"""
        if not os.path.exists(InstallConfig.SRC):
            os.makedirs(InstallConfig.SRC)
            Logger.log(f"创建源码目录: {InstallConfig.SRC}")

    @staticmethod
    def install_geppetto(branch='development'):
        """安装 Geppetto 组件"""
        Logger.header("安装 Geppetto")

        ComponentInstaller.setup_source_dir()

        # 克隆 geppetto-meta
        meta_dir = os.path.join(InstallConfig.SRC, InstallConfig.COMPONENTS['geppetto_meta'])
        if not GitManager.clone_repo(InstallConfig.REPOS['geppetto_meta'], meta_dir, branch):
            Logger.log("克隆 Geppetto 失败", 'error')
            return False

        # 安装 pygeppetto
        pygeppetto_dir = os.path.join(meta_dir, InstallConfig.COMPONENTS['pygeppetto'])
        if os.path.exists(pygeppetto_dir):
            Logger.log("安装 pygeppetto")
            CommandRunner.run(['pip', 'install', '-e', '.'], cwd=pygeppetto_dir)

        # 安装 jupyter-geppetto
        jupyter_gep_dir = os.path.join(meta_dir, InstallConfig.COMPONENTS['jupyter_geppetto'])
        if os.path.exists(jupyter_gep_dir):
            Logger.log("安装 jupyter-geppetto")
            CommandRunner.run(['pip', 'install', '-e', '.'], cwd=jupyter_gep_dir)

        # 重新安装核心依赖
        CommandRunner.run(['pip', 'install', '-e', '.'], cwd=InstallConfig.ROOT)

        Logger.log("Geppetto 安装成功", 'success')
        return True

    @staticmethod
    def install_神经网络工具(branch='development'):
        """从源码安装 神经网络工具"""
        Logger.header("安装 神经网络工具")

        ComponentInstaller.setup_source_dir()

        神经网络工具_dir = os.path.join(InstallConfig.SRC, InstallConfig.COMPONENTS['神经网络工具'])
        if not GitManager.clone_repo(InstallConfig.REPOS['神经网络工具'], 神经网络工具_dir, branch):
            Logger.log("克隆 神经网络工具 失败", 'error')
            return False

        Logger.log("安装 神经网络工具包")
        CommandRunner.run(['pip', 'install', '-e', '.'], cwd=神经网络工具_dir)
        Logger.log("神经网络工具 安装成功", 'success')
        return True

    @staticmethod
    def install_workspace(branch='master'):
        """克隆工作空间仓库"""
        Logger.header("安装工作空间")

        workspace_dir = os.path.join(InstallConfig.ROOT, InstallConfig.COMPONENTS['workspace'])
        if not GitManager.clone_repo(InstallConfig.REPOS['workspace'], workspace_dir, branch):
            Logger.log("克隆工作空间失败", 'error')
            return False

        Logger.log("工作空间克隆成功", 'success')
        return True


class JupyterConfigurator:
    """Jupyter 配置器"""

    @staticmethod
    def configure():
        """为  配置 Jupyter"""
        Logger.header("配置 Jupyter")

        commands = [
            ['jupyter', 'serverextension', 'enable', '--py', 'jupyter_geppetto'],
            ['jupyter', 'nbextension', 'install', '--py', 'jupyter_geppetto'],
            ['jupyter', 'nbextension', 'enable', '--py', 'jupyter_geppetto'],
            ['jupyter', 'nbextension', 'enable', '--py', 'widgetsnbextension']
        ]

        for cmd in commands:
            CommandRunner.run(cmd, check=False)

        Logger.log("Jupyter 配置成功", 'success')


class WebAppBuilder:
    """Web 应用构建器"""

    @staticmethod
    def build(geppetto_installed=False, dev_mode=False):
        """使用 npm/yarn 构建 Web 应用"""
        Logger.header("构建 Web 应用")

        if not os.path.exists(InstallConfig.WEBAPP):
            Logger.log("Web 应用目录未找到，跳过构建", 'warning')
            return

        if geppetto_installed:
            WebAppBuilder._build_with_geppetto(dev_mode)
        else:
            WebAppBuilder._build_standard(dev_mode)

    @staticmethod
    def _build_with_geppetto(dev_mode):
        """使用 Geppetto 集成构建"""
        Logger.log("使用 Geppetto 集成构建")

        # 清理 .yalc 目录（如果存在）
        yalc_dir = os.path.join(InstallConfig.WEBAPP, '.yalc')
        if os.path.exists(yalc_dir):
            shutil.rmtree(yalc_dir)

        # 运行 geppetto UI 脚本
        CommandRunner.run(['bash', 'geppetto_ui.sh'], cwd=InstallConfig.WEBAPP, check=False)

        # 安装并构建
        CommandRunner.run(['yarn'], cwd=InstallConfig.WEBAPP, check=False)
        build_cmd = 'build-dev' if dev_mode else 'build'
        CommandRunner.run(['yarn', 'run', build_cmd], cwd=InstallConfig.WEBAPP, check=False)

    @staticmethod
    def _build_standard(dev_mode):
        """标准构建（不含 Geppetto）"""
        Logger.log("标准 Web 应用构建")

        CommandRunner.run(['yarn', 'install', '--immutable'], cwd=InstallConfig.WEBAPP, check=False)
        build_cmd = 'build' if not dev_mode else 'build-dev'
        CommandRunner.run(['yarn', 'run', build_cmd], cwd=InstallConfig.WEBAPP, check=False)


class TestRunner:
    """测试运行器"""

    @staticmethod
    def run_tests():
        """执行测试套件"""
        Logger.header("运行测试")

        # 安装测试依赖
        test_file = os.path.join(InstallConfig.ROOT, InstallConfig.TEST_FILE)
        if os.path.exists(test_file):
            CommandRunner.run(['pip', 'install', '-r', InstallConfig.TEST_FILE])

        # 运行 pytest
        CommandRunner.run(['python', '-m', 'pytest', 'tests'], check=False)
        Logger.log("测试完成", 'success')


# ============================================
# 主安装协调器
# ============================================

def main():
    """主安装函数"""
    parser = argparse.ArgumentParser(
        description=' 安装管理器 (Python 3.11+)',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  python install_python311.py
  python install_python311.py --神经网络工具 development
  python install_python311.py --geppetto development --skip-npm
  python install_python311.py --神经网络工具 dev --geppetto dev --workspace master
        """
    )

    parser.add_argument('--神经网络工具', dest='神经网络工具_branch', metavar='分支',
                       help='从指定分支安装 神经网络工具 (例如: development, master)')
    parser.add_argument('--geppetto', dest='geppetto_branch', metavar='分支',
                       help='从指定分支安装 Geppetto (例如: development, master)')
    parser.add_argument('--workspace', dest='workspace_branch', metavar='分支',
                       help='从指定分支克隆工作空间 (默认: master)')
    parser.add_argument('--skip-npm', action='store_true',
                       help='跳过 npm/yarn 构建步骤')
    parser.add_argument('--skip-test', action='store_true',
                       help='跳过运行测试')
    parser.add_argument('--dev', dest='development', action='store_true',
                       help='开发模式 (使用 build-dev 而不是 build)')

    args = parser.parse_args()

    try:
        Logger.header(" 安装开始")
        Logger.log(f"Python 版本: {sys.version}")
        Logger.log(f"安装目录: {InstallConfig.ROOT}")

        # 步骤 1: 安装依赖
        DependencyInstaller.install_requirements()

        # 步骤 2: 安装 UI包
        DependencyInstaller.install_ui_package()

        # 步骤 3: 安装可选组件
        geppetto_installed = False
        if args.geppetto_branch:
            branch = args.geppetto_branch if args.geppetto_branch.strip() else 'development'
            geppetto_installed = ComponentInstaller.install_geppetto(branch)

        if args.神经网络工具_branch:
            branch = args.神经网络工具_branch if args.神经网络工具_branch.strip() else 'development'
            ComponentInstaller.install_神经网络工具(branch)

        if args.workspace_branch:
            branch = args.workspace_branch if args.workspace_branch.strip() else 'master'
            ComponentInstaller.install_workspace(branch)

        # 步骤 4: 配置 Jupyter
        JupyterConfigurator.configure()

        # 步骤 5: 构建 Web 应用
        if not args.skip_npm:
            WebAppBuilder.build(geppetto_installed, args.development)
        else:
            Logger.log("跳过 Web 应用构建 (--skip-npm)", 'warning')

        # 步骤 6: 运行测试
        if not args.skip_test:
            TestRunner.run_tests()
        else:
            Logger.log("跳过测试 (--skip-test)", 'warning')

        Logger.header("安装成功完成！")
        Logger.log("现在可以开始使用  了", 'success')

    except KeyboardInterrupt:
        Logger.log("\n用户取消安装", 'warning')
        sys.exit(1)
    except Exception as e:
        Logger.log(f"安装失败: {e}", 'error')
        sys.exit(1)


if __name__ == "__main__":
    main()

